﻿#region "Copyright (C) Lenny Granovsky. 2011-2013"
//    This library provides extended cache capabilities to the ASP.NET applications.
//
//                Copyright (C) Lenny Granovsky. 2011-2013. 
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 2 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//    This program comes with ABSOLUTELY NO WARRANTY.
//    This is free software, and you are welcome to redistribute it
//    under certain conditions;
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using FlexiCache.Utilities;

namespace FlexiCache
{
    /// <summary>
    /// Class provides functionality for cleaning up the out-of-process cache.
    /// </summary>
    public class CacheCleanupUtility : IDisposable
    {
        private static System.Timers.Timer ScheduleTimer1;

        private void InitializeComponent()
        {
            if (ScheduleTimer1 == null)
            {
                ScheduleTimer1 = new System.Timers.Timer();
                ((System.ComponentModel.ISupportInitialize)ScheduleTimer1).BeginInit();
                ScheduleTimer1.AutoReset = false;
                ((System.ComponentModel.ISupportInitialize)ScheduleTimer1).EndInit();
            }
            ScheduleTimer1.Stop();
        }

        protected CacheCleanupUtility()
        {
        }

        /// <summary>
        /// Method creates an instance of this class.
        /// </summary>
        public static void CreateInstance()
        {
            if (CachingConfiguration.Current.IsCacheCleanupEnabled)
            {
                if (System.Web.HttpContext.Current == null)
                    throw new Exception("Cannot initialize CacheCleanupUtility because HttpContext is empty.");
                else
                {
                    CacheCleanupUtility cacheCleaner = new CacheCleanupUtility();
                    cacheCleaner.InitializeComponent();
                    cacheCleaner.StartCacheCleanupScheduler();
                    System.Web.HttpContext.Current.Application["CacheCleanupUtilityHolder"] = cacheCleaner;
                }
            }
        }

        private void StartCacheCleanupScheduler()
        {
            try
            {
                if (CachingConfiguration.Current.IsCacheCleanupEnabled)
                {
                    int interval = CachingConfiguration.Current.CacheCleanupInterval <= 30 ? 30 : CachingConfiguration.Current.CacheCleanupInterval;
                    System.Diagnostics.Trace.WriteLine(string.Format("CacheCleanupUtility.StartCacheCleanupSheduler. CacheCleanup process is enabled with {0} sec.", interval.ToString()));
                    ((System.ComponentModel.ISupportInitialize)ScheduleTimer1).BeginInit();
                    ScheduleTimer1.Interval = interval * 1000;
                    ScheduleTimer1.Enabled = true;
                    ScheduleTimer1.AutoReset = false;
                    ScheduleTimer1.Elapsed += new System.Timers.ElapsedEventHandler(ScheduleTimer1_Elapsed);
                    ((System.ComponentModel.ISupportInitialize)ScheduleTimer1).EndInit();
                    ScheduleTimer1.Start();
                }
                else
                {
                    System.Diagnostics.Trace.WriteLine("CacheCleanupUtility.StartCacheCleanupSheduler. CacheCleanup process is disabled.");
                    ScheduleTimer1.Enabled = false;
                }
            }
            catch (Exception ex)
            {
                ErrorUtility.HandleExceptionInternally(ex);
            }
        }

        private void ScheduleTimer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            try
            {
                System.Threading.Thread oThread = new System.Threading.Thread(new System.Threading.ThreadStart(CleanupCacheStorage));
                oThread.Name = "FlexiCache out-of-process Cache storage cleanup";
                oThread.Priority = System.Threading.ThreadPriority.BelowNormal;
                oThread.Start();
                //wait for other thread
                oThread.Join();
            }
            catch (ThreadAbortException)
            {
                //do nothing
            }
            catch (Exception ex)
            {
                ErrorUtility.HandleExceptionInternally(ex);
            }
            finally
            {
                ScheduleTimer1.Start();
            }
        }

        private void CleanupCacheStorage()
        {
            try
            {
                System.Diagnostics.Trace.WriteLine("CacheCleanupUtility.CleanupCacheStorage process started.");
                IEnumerator<IExpirationProvider> workflow = this.CreateWorkflow();
                this.ExecuteWorkflow(workflow);
                System.Diagnostics.Trace.WriteLine("CacheCleanupUtility.CleanupCacheStorage process ended.");

                
            }
            catch (Exception ex)
            {
                ErrorUtility.HandleExceptionInternally(ex);
            }
            try
            {
                Thread.CurrentThread.Abort();
            }
            catch (ThreadAbortException)
            {
                //do nothing
            }
            catch (Exception ex2)
            {
                ErrorUtility.HandleExceptionInternally(ex2);
            }
        }

        internal IEnumerator<IExpirationProvider> CreateWorkflow()
        {
            yield return new ExternalOutputCacheProvider();
            yield return new CacheOnDemandProvider();
            yield return new SessionOnDemandProvider();
        }

        internal void ExecuteWorkflow(IEnumerator<IExpirationProvider> workflow)
        {
            while (workflow.MoveNext())
            {
                workflow.Current.RemoveExpired();
                System.Diagnostics.Trace.TraceInformation("{0} executed.", workflow.Current.ToString());
            }
        }

        private void DisposeComponent()
        {
            if (ScheduleTimer1 != null)
            {
                ScheduleTimer1.Stop();
                ScheduleTimer1.Dispose();
            }
        }

        public void Dispose()
        {
            if (System.Web.HttpContext.Current != null)
            {
                if (System.Web.HttpContext.Current.Application["CacheCleanupUtilityHolder"] != null)
                {
                    CacheCleanupUtility cacheCleaner = (CacheCleanupUtility)System.Web.HttpContext.Current.Application["CacheCleanupUtilityHolder"];
                    cacheCleaner.DisposeComponent();
                    cacheCleaner.StartCacheCleanupScheduler();
                    System.Web.HttpContext.Current.Application["CacheCleanupUtilityHolder"] = null;
                }
            }
        }
    }
}
